package linkedlist;

/**
 * Created by Tzachi on 18/10/2014.
 */
public class MyLinkedList<E> {
    private int size = 0;
    private Node<E> first;
    private Node<E> last;

    // add item an the end of the list
    public void add(E value) {
        Node<E> node = new Node<>(value);
        if (first == null && last == null) {
            this.first = node;
        } else {
            this.last.next = node;
        }
        this.last = node;
        size++;
    }

    // add item in a specific index
    public void add(E value, int index) {
        Node<E> node = new Node<>(value);
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException();
        }
        if (index == size) {
            add(value);
        }
        Node<E> beforeInsertion = getBeforeIndexNode(index);
        node.next = beforeInsertion.next;
        beforeInsertion.next = node;
        size++;
    }

    // remove a node from a specific index
    public E remove(int index) {
        if (first == null) {
            new RuntimeException("empty list");
        }
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException();
        }
        Node<E> beforeInsertion = getBeforeIndexNode(index);
        Node<E> nodeToRemove = beforeInsertion.getNext();
        beforeInsertion.next = nodeToRemove.next;
        nodeToRemove.next = null;
        size--;
        return nodeToRemove.getValue();
    }

    private Node<E> getBeforeIndexNode(int index) {
        Node<E> before = this.first;
        for (int i = 0; i < index; i++) {
            before = before.next;
        }
        return before;
    }

    public int getSize() {
        return size;
    }

    static class Node<E> {
        private final E value;
        private Node<E> next;

        Node(E value) {
            this.value = value;
        }

        public E getValue() {
            return value;
        }

        public Node<E> getNext() {
            return next;
        }
    }
}
